/*	$Id: pbuf.h 7 2008-07-29 02:58:11Z phrakt $	*/
/*
 * Copyright (c) 2003, 2005 Jean-Francois Brousseau <jfb@openbsd.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 */

#ifndef _PFORGE_PBUF_H_
#define _PFORGE_PBUF_H_

#include <sys/types.h>

#include <pforge/queue.h>


#define PFPBUF_SIZE           128
#define PFPBUF_CLSIZE        2048
#define PFPBUF_DATA_SIZE     (PFPBUF_SIZE - sizeof(struct pf_pbuf_hdr))
#define PFPBUF_CLSDATA_SIZE  (PFPBUF_CLSIZE - sizeof(struct pf_pbuf_hdr))


/* pbuf flags */
#define PFPBUF_HEAD   0x01	/* head of chain */
#define PFPBUF_CLSTR  0x02	/* pbuf has associated cluster */


/* pbuf op flags */
#define PFPBUF_OP_NOBLOCK  0x01   /* don't block for available memory */


struct pf_pbuf_hdr {
	uint16_t             pb_flags;
	uint16_t             pb_len;	/* length of valid data */
	void                *pb_start;	/* start of data */
	TAILQ_ENTRY(pf_pbuf) pb_link;
};


typedef struct pf_pbuf {
	struct pf_pbuf_hdr pb_hdr;
	uint8_t            pb_data[PFPBUF_DATA_SIZE];
} PF_PBUF;

typedef struct pf_pchain {
	TAILQ_HEAD(, pf_pbuf) pc_chain;
	int             pc_len;		/* length (in # of pbufs) */
	int             pc_size;	/* total size of data in bytes */
} PF_PCHAIN;



/*
 * Determine the amount of leading space available before the used portion
 * of the pbuf <p>.
 */
#define PFPBUF_LDSPACE(p)  \
	((size_t)(p)->pb_data - (size_t)(p)->pb_hdr.pb_start)

/*
 * Determine the amount of trailing space left after the used portion
 * of the pbuf <p>.
 */
#define PFPBUF_TRSPACE(p)  \
	(PFPBUF_DATA_SIZE - (p)->pb_hdr.pb_len - \
	    ((size_t)(p)->pb_hdr.pb_start - (size_t)(p)->pb_data))

#define PFPBUF_CLR(p)					\
	do {						\
		(p)->pb_hdr.pb_start = (p)->pb_data;	\
		(p)->pb_hdr.pb_len = 0;			\
	} while (0)


#define PFPBUF_START(p)		((p)->pb_hdr.pb_start)
#define PFPBUF_ISHEAD(p)	((p)->pb_hdr.pb_flags & PFPBUF_HEAD)

/*
 * Check if the data starting at offset <s> and of length <l> is contiguous
 * within the pbuf <p>.
 */
#define PFPBUF_CONTIG(p, s, l)		/* XXX fix me */

/*
 * Initialize a pbuf chain.
 */
#define PFPCHAIN_INIT(pc)			\
	do {					\
		(pc)->pc_len = 0;		\
		(pc)->pc_size = 0;		\
		TAILQ_INIT(&((pc)->pc_chain));	\
	} while (0)

#define PFPCHAIN_HEAD(pc)	TAILQ_FIRST(&(pc)->pc_chain)
#define PFPCHAIN_TAIL(pc)	TAILQ_END(&(pc)->pc_chain)
#define PFPCHAIN_LEN(pc)	((pc)->pc_len)
#define PFPCHAIN_EMPTY(pc)	((pc)->pc_len == 0)


/*
 * Attach the buffer <b> to the head of the pbuf chain <c>.
 */
#define PFPCHAIN_PREPEND(c, b)						\
	do {								\
		PF_PBUF *head;						\
		head = PFPCHAIN_HEAD(c);				\
		head->pb_hdr.pb_flags &= ~PFPBUF_HEAD;			\
		b->pb_hdr.pb_flags |= PFPBUF_HEAD;			\
		TAILQ_INSERT_HEAD(&((c)->pc_chain), b, pb_hdr.pb_link);	\
		(c)->pc_len++;						\
		(c)->pc_size += b->pb_hdr.pb_len;			\
	} while (0)

/*
 * Attach the buffer <b> to the tail of the pbuf chain <c>.
 */
#define PFPCHAIN_APPEND(c, b)						\
	do {								\
		TAILQ_INSERT_TAIL(&((c)->pc_chain), b, pb_hdr.pb_link);	\
		(c)->pc_len++;						\
		(c)->pc_size += b->pb_hdr.pb_len;			\
	} while (0)

#define PFPCHAIN_FOREACH(b, c)						\
	for ((b) = TAILQ_FIRST(&((c)->pc_chain));			\
	    (b) != TAILQ_END(&((c)->pc_chain));				\
	    (b) = TAILQ_NEXT((b), pb_hdr.pb_link))

int       pf_pbuf_init    (void);
void      pf_pbuf_cleanup (void);
PF_PBUF*  pf_pbuf_alloc   (int);
void      pf_pbuf_free    (PF_PBUF *);

#endif	/* _PFORGE_PBUF_H_ */
